package com.zwj.interview.缓存;

import java.util.HashMap;
import java.util.Map;

/**
 * 请设计实现一个最近最少使用（Least Recently Used，LRU）缓存，要求如下两个操作的时间复杂度都是O（1）。
 * ● get（key）：如果缓存中存在键key，则返回它对应的值；否则返回-1。
 * ● put（key，value）：如果缓存中之前包含键key，则它的值设为value；
 * 否则添加键key及对应的值value。在添加一个键时，如果缓存容量已经满了，
 * 则在添加新键之前删除最近最少使用的键（缓存中最长时间没有被使用过的元素）
 */
public class LRUCache {

    //head和tail是哨兵节点，为了简化链表的操作
    private ListNode head;
    private ListNode tail;
    //盛放数据的容器
    private Map<Integer, ListNode> map;
    int capacity;


    public LRUCache(int cap) {
        map = new HashMap<>();
        head = new ListNode(-1, -1);
        tail = new ListNode(-1, -1);
        head.next = tail;
        tail.prev = head;
        capacity = cap;
    }


    class ListNode {
        public int key;
        public int value;

        public ListNode next;
        public ListNode prev;

        public ListNode(int k, int v) {
            key = k;
            value = v;
        }
    }


    public int get(int key) {
        ListNode node = map.get(key);
        if (node == null) {
            return -1;
        }
        //将该节点移动到链表尾部
        moveToTail(node, node.value);
        return node.value;
    }

    /**
     * 如果说存在该key了，直接将该节点移动到尾部，用新值代替老值
     * 不存在该key的话，如果容量满了，则找到要删除的一个，删除后，再将增加的值移动到链表尾部
     *
     * @param key
     * @param value
     */
    public void put(int key, int value) {
        if (map.containsKey(key)) {
            moveToTail(map.get(key), value);
        } else {
            //已经达到容量了
            if (map.size() == capacity) {
                //找到最近没有使用过的数据
                ListNode toBeDeleted = head.next;
                deleteNode(toBeDeleted);
                //删除数据
                map.remove(toBeDeleted.key);
            }
            ListNode node = new ListNode(key, value);
            insertToTail(node);
            map.put(key, node);
        }
    }

    //将node移动到链表尾部
    public void moveToTail(ListNode node, int newValue) {
        //先删除node(不是真的删)
        deleteNode(node);
        node.value = newValue;
        insertToTail(node);

    }

    //删除传入的节点(维护双向指针)
    public void deleteNode(ListNode node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    public void insertToTail(ListNode node) {
        tail.prev.next = node;
        node.prev = tail.prev;
        node.next = tail;
        tail.prev = node;
    }


}
